;*****************************************************************************************************
;+
; NAME:
;
;      eAXIS (enhanced AXIS)
;
; PURPOSE:
;
;      This is the enhanced version of the standard IDL AXIS
;      command. It uses the same syntax and support most of the IDL
;      plotting keywords. The output axis is displayed in an IGL window
;      so that you can easily interact with it.
;
;      NOTE: The axis always has exact ranges. Extended ranges as
;            calculated by IDL would prevent the pan and zoom interactions to
;            work correctly. In most cases, having exact ranges is what
;            is desired anyway.
;
; CALLING SEQUENCE:
;
;      eAXIS, x, y
;
; ARGUMENTS:
;
;     X:            The starting X coordinate of the new axis. If not
;                   specified, the axis is positioned according to the
;                   XAXIS or YAXIS parameters. Note that this value is
;                   ignored when creating an X axis.
;
;     Y:            The starting Y coordinate of the new axis. If not
;                   specified, the axis is positioned according to the
;                   XAXIS or YAXIS parameters. Note that this value is
;                   ignored when creating a Y axis.
;
;                 
; KEYWORD PARAMETERS:
;
;     XAXIS:        Set this keyword to draw an X axis. If the X
;                   argument is not present, XAXIS=0 draw a bottom
;                   axis and XAXIS=1 draw a top axis. Otherwise,
;                   XAXIS=0 draw an X axis with tick marks pointing up
;                   and XAXIS=1 draw an X axis with tick marks
;                   pointing down.
; 
;     YAXIS:        Set this keyword to draw a Y axis. If the Y
;                   argument is not present, YAXIS=0 draw a left
;                   axis and YAXIS=1 draw a right axis. Otherwise,
;                   YAXIS=0 draw a Y axis with tick marks pointing right
;                   and YAXIS=1 draw a Y axis with tick marks
;                   pointing left.
;
;     SAVE:         Set this keyword to save the newly established
;                   coordinate system. Subsequent IGL overplotting
;                   commands will use the axis coordinate system.
;
;     CHARSIZE:     The character size for the annotations (float). The default
;                   is !IGL_P.charsize
;
;     CHARTHICK:    The character thickness for the annotations (integer). The
;                   default is !IG_P.charthick
;
;     COLOR:        The name of the foreground color for the axis. The
;                   default is !IGL_P.axiscolor
;
;     DATA:         Set this keyword to indicate that positioning
;                   parameters are given in data coordinates. This is
;                   the default.
;
;     DEVICE:       Set this keyword to indicate that positioning
;                   parameters are given in device coordinates.
;
;     FONT:         The character font for the annotations(integer).
;                   -1 : Herschey, 0 : Hardware, 1 : True Type. The
;                   default is !IGL_P.font
;
;     NODRAW:       *IGL ADDITION* : Set this keyword to prevent the
;                   current window to be refreshed. The axis
;                   is added to the window but will not be
;                   visible until the window is refreshed (by calling
;                   another IGL plotting command for example). This is
;                   useful is you wish to issue several plotting
;                   commands but want to display the results only at
;                   the end. 
;
;     NORMAL:       Set this keyword to indicate that positioning
;                   parameters are given in normalized coordinates.
;
;     STATIC:       *IGL ADDITION* : Set this keyword to indicate that
;                   the data coordinate system associated with the
;                   axis is static. This prevents the data X and Y
;                   ranges to be changed.
;
;     TICKLEN:      Specify the length of the axis tick marks, in
;                   units of the fraction of the plot window. The
;                   default is !IGL_P.ticklen.
;
;     [XY]CHARSIZE: The character size for the axis annotations. Be
;                   careful that this is like the regular IDL
;                   keyword meaning that it only indicates a multiplying
;                   factor to the global character size specified by
;                   the CHARSIZE keyword. The default is !IGL_[XY].charsize
;
;     [XY]GRIDSTYLE: Integer specifying the linestyle to use for the
;                    axis grid. The default is !IGL_[XY].gridstyle
;
;     [XY]LOG:      Set this keyword if you want logarithmic axis
;                   scaling.
;
;     [XY]MINOR:    The number of minor axis tick intervals. The default
;                   is !IGL_[XY].minor
;
;     [XY]RANGE:    2-element vector specifying the axis data
;                   range. The default is !IGL_[XY].range
;
;     [XY]THICK:    The thickness of the line forming the axis. The
;                   default is !IGL_[XY].thick
;
;     [XY]TICKFORMAT: String specifying the format used for the axis
;                     labels. The default is !IGL_[XY].tickformat
;
;     [XY]TICKINTERVAL: Scalar indicating the interval between major
;                       tick marks. The default is !IGL_[XY].tickinterval
;
;     [XY]TICKLAYOUT: Integer specifying the axis layout. Possible
;                     values are : 0 = Normal layout, 1 = Only labels,
;                     2 = Box layout. The default is !IGL_[XY].ticklayout
;
;     [XY]TICKLEN:  The length of the axis tick marks. This overrides
;                   the global setting specified by the TICKLEN
;                   keyword. The default is !IGL_[XY].ticklen
;
;     [XY]TICKNAME: String array specifying the annotation of each
;                   tick marks. The default is !IGL_[XY].tickname
;
;     [XY]TICKS:    Integer specifying the number of major tick
;                   intervals. The default is !IGL_[XY].ticks
;
;     [XY]TICKUNITS: Vector of strings indicating the units used for
;                    axis tick labeling. The default is !IGL_[XY].tickunits
;
;     [XY]TICKV:    Array of data values for each tick mark. The
;                   default is !IGL_[XY].tickv
;
;     [XY]TITLE:    The text string used as the axis title. The
;                   default is !IGL_[XY].title
;    
; PACKAGE:
;
;    Interactive Graphics Library (IGL)
;
; AUTHORS:
;
;        RAPHAEL SADOUN
;        Graduate Student
;        Institut d'Astrophysique de Paris
;        98 bis boulevard Arago
;        75014 PARIS FRANCE
;        E-mail: raphael.sadoun@gmail.com
;
;
; MODIFICATION HISTORY:
;
;     Written by: Raphael Sadoun, 24 April 2012
;-
;*****************************************************************************************************
PRO eAXIS, x, y, $
           XAXIS=xaxis, $
           YAXIS=yaxis, $
           SAVE=save, $
           CHARSIZE=charsize, $
           CHARTHICK=charthick, $
           COLOR=color, $
           DEVICE=device, $
           DATA=data, $
           FONT=font, $
           NODRAW=nodraw, $
           NORMAL=normal, $
           STATIC=static, $
           TICKLEN=ticklen, $
           XCHARSIZE=xcharsize, $
           XGRIDSTYLE=xgridstyle, $
           XLOG=xlog, $
           XMINOR=xminor, $
           XRANGE=xrange, $
           XTHICK=xthick, $
           XTICKFORMAT=xtickformat, $
           XTICKINTERVAL=xtickinterval, $
           XTICKLAYOUT=xticklayout, $
           XTICKLEN=xticklen, $
           XTICKNAME=xtickname, $
           XTICKS=xticks, $
           XTICKUNITS=xtickunits, $
           XTICKV=xtickv, $
           XTITLE=xtitle, $
           YCHARSIZE=ycharsize, $
           YGRIDSTYLE=ygridstyle, $
           YLOG=ylog, $
           YMINOR=yminor, $
           YRANGE=yrange, $
           YTHICK=ythick, $
           YTICKFORMAT=ytickformat, $
           YTICKINTERVAL=ytickinterval, $
           YTICKLAYOUT=yticklayout, $
           YTICKLEN=yticklen, $
           YTICKNAME=ytickname, $
           YTICKS=yticks, $
           YTICKUNITS=ytickunits, $
           YTICKV=ytickv, $
           YTITLE=ytitle

  COMPILE_OPT idl2, HIDDEN
  ON_ERROR, 2

  ;; Initialize the global system variables
  IGL_InitSysVars

  ;; Check positional parameters
  CASE N_PARAMS() OF
     
     1: BEGIN
        xloc = x
     END
     
     2: BEGIN
        xloc = x
        yloc = y
     END

     ELSE:

  ENDCASE

  ;; Default keyword values

  ;; General keywords
  IF (N_Elements(xaxis) EQ 0) AND (N_Elements(yaxis) EQ 0) THEN xaxis = 0
  IF N_Elements(charsize) EQ 0 THEN charsize = (!IGL_P.charsize LE 0) ? 1 : !IGL_P.charsize
  IF N_Elements(charthick) EQ 0 THEN charthick = (!IGL_P.charthick LE 0) ? 1 : !IGL_P.charthick
  IF N_Elements(color) EQ 0 THEN color = !IGL_P.axiscolor
  IF N_Elements(font) EQ 0 THEN font = !IGL_P.font
  IF N_Elements(ticklen) EQ 0 THEN ticklen = (!IGL_P.ticklen LE 0) ? 0.02 : !IGL_P.ticklen

  ;; X axis keywords
  IF N_Elements(xcharsize) EQ 0 THEN xcharsize = (!IGL_X.charsize LE 0) ? 1 : !IGL_X.charsize
  IF N_Elements(xgridstyle) EQ 0 THEN xgridstyle = !IGL_X.gridstyle
  IF N_Elements(xminor) EQ 0 THEN xminor = !IGL_X.minor
  IF N_Elements(xrange) EQ 0 THEN xrange = !IGL_X.range
  IF N_Elements(xthick) EQ 0 THEN xthick = (!IGL_X.thick LE 0) ? 1 : !IGL_X.thick
  IF N_Elements(xtickformat) EQ 0 THEN xtickformat = !IGL_X.tickformat
  IF N_Elements(xtickinterval) EQ 0 THEN xtickinterval = !IGL_X.tickinterval
  IF N_Elements(xticklayout) EQ 0 THEN xticklayout = !IGL_X.ticklayout
  IF N_Elements(xticklen) EQ 0 THEN xticklen = (!IGL_X.ticklen LE 0) ? ticklen : !IGL_X.ticklen
  IF N_Elements(xtickname) EQ 0 THEN xtickname = !IGL_X.tickname
  IF N_Elements(xticks) EQ 0 THEN xticks = !IGL_X.ticks
  IF N_Elements(xtickunits) EQ 0 THEN xtickunits = !IGL_X.tickunits
  IF N_Elements(xtickv) EQ 0 THEN xtickv = !IGL_X.tickv
  IF N_Elements(xtitle) EQ 0 THEN xtitle = !IGL_X.title

  ;; Y axis keywords
  IF N_Elements(ycharsize) EQ 0 THEN ycharsize = (!IGL_Y.charsize LE 0) ? 1 : !IGL_Y.charsize
  IF N_Elements(ygridstyle) EQ 0 THEN ygridstyle = !IGL_Y.gridstyle
  IF N_Elements(yminor) EQ 0 THEN yminor = !IGL_Y.minor
  IF N_Elements(yrange) EQ 0 THEN yrange = !IGL_Y.range
  IF N_Elements(ythick) EQ 0 THEN ythick = (!IGL_Y.thick LE 0) ? 1 : !IGL_Y.thick
  IF N_Elements(ytickformat) EQ 0 THEN ytickformat = !IGL_Y.tickformat
  IF N_Elements(ytickinterval) EQ 0 THEN ytickinterval = !IGL_Y.tickinterval
  IF N_Elements(yticklayout) EQ 0 THEN yticklayout = !IGL_Y.ticklayout
  IF N_Elements(yticklen) EQ 0 THEN yticklen = (!IGL_Y.ticklen LE 0) ? ticklen : !IGL_Y.ticklen
  IF N_Elements(ytickname) EQ 0 THEN ytickname = !IGL_Y.tickname
  IF N_Elements(yticks) EQ 0 THEN yticks = !IGL_Y.ticks
  IF N_Elements(ytickunits) EQ 0 THEN ytickunits = !IGL_Y.tickunits
  IF N_Elements(ytickv) EQ 0 THEN ytickv = !IGL_Y.tickv
  IF N_Elements(ytitle) EQ 0 THEN ytitle = !IGL_Y.title

  ;; Save device settings and switch to window device
  entryDevice = !d.name
  IF (strupcase(!version.os_family) EQ 'UNIX') THEN set_plot, 'X'
  IF (strupcase(!version.os_family) EQ 'WINDOWS') THEN set_plot, 'WIN'

  ;; Get current window
  currentWindow = IGL_GetCurrentWindow()
  IF OBJ_ISA_VALID(currentWindow,'IGL_WINDOW') EQ 0 THEN Message, 'No valid IGL window available'
  currentWindow -> GetProperty, Window_Xsize=Xsize, Window_Ysize=Ysize

  ;; Get the current coordinate system
  currentCoords = IGL_GetSysVars('currentCoord')
  IF OBJ_ISA_VALID(currentCoords,'CatCoord') EQ 0 THEN Message, 'A coordinate system is not established'
  currentCoords -> GetProperty, Position=pos, Xrange=xr, Yrange=yr, Xlog=xl, Ylog=yl, Static=IsStatic
     
  ;; Get the object that represents the coordinate system in the graphic
  currentCoords -> GetProperty, FIRST_PARENT=parent
  IF OBJ_VALID(parent) EQ 0 THEN Message, 'Invalid coordinate system'
  
  ;; Get the graphic in which the parent object is contained
  parent -> GetProperty, FIRST_PARENT=graphic
  IF OBJ_ISA_VALID(graphic,'IGL_Graphic') EQ 0 THEN message, 'Current coordinate system is not in a valid graphic'

  ;; Make sure we have valid X and Y ranges
  IF xrange[0] EQ xrange[1] THEN xrange = xr
  IF yrange[0] EQ yrange[1] THEN yrange = yr

  ;; Create the axis  
  IF N_Elements(xaxis) NE 0 THEN BEGIN ; Here we create an X axis

     ;; Do we need a new coordinate system ?
     IF (ARRAY_EQUAL(xrange,xr) EQ 0) OR (Keyword_Set(xlog) NE Keyword_Set(xl)) OR (Keyword_Set(static) NE IsStatic) THEN BEGIN
        
        coords = obj_new('IGL_Coord', $
                         POSITION=pos, $
                         XRANGE=xrange, $
                         YRANGE=yr, $
                         XLOG=Keyword_Set(xlog), $
                         YLOG=yl, $
                         STATIC=Keyword_Set(static) $
                        )

     ENDIF ELSE coords = currentCoords

     ;; Calculate the position of the axis in the graphic
     IF N_Elements(yloc) NE 0 THEN BEGIN

        ;; First convert to normalized coordinates        
        IF Keyword_Set(Device) THEN ypos = yloc/float(Ysize)

        IF Keyword_Set(Normal) THEN ypos = yloc

        IF Keyword_Set(Data) OR (N_Elements(ypos) EQ 0) THEN ypos = (yr[1]-yr[0])/(pos[3]-pos[1])*yloc

        ;; Now convert to graphic coordinates
        ypos = (ypos-pos[1])/(pos[3]-pos[1])
        
     ENDIF ELSE BEGIN

        IF xaxis EQ 0 THEN ypos = 0.0
        IF xaxis EQ 1 THEN ypos = 1.0

     ENDELSE

     ;; Create the axis object
     axis = Obj_New('IGL_Axis', $
                    COORD_OBJECT=coords, $
                    XLOC=xpos, $
                    YLOC=ypos, $
                    DIRECTION=0, $
                    TICKDIR=Keyword_Set(xaxis), $
                    COLOR=color, $
                    CHARSIZE=(xcharsize*charsize), $
                    CHARTHICK=charthick, $
                    FONT=(font+1), $
                    GRIDSTYLE=xgridstyle, $
                    THICK=xthick, $
                    MINOR=xminor, $
                    TICKFORMAT=xtickformat, $
                    TICKNAME=xtickname, $
                    TICKINTERVAL=xtickinterval, $
                    TICKLAYOUT=xticklayout, $
                    TICKLEN=xticklen, $
                    TICKS=xticks, $
                    TICKUNITS=xtickunits, $
                    TICKV=xtickv $
                   )

     ;; Add a title if requested
     IF xtitle NE '' THEN BEGIN

         ;; Calculate the title position
        xloc = 0.5
        yloc = pos[1]-3.0*charsize*xcharsize*!D.Y_CH_SIZE/float(Ysize)
        yloc = (yloc-pos[1])/(pos[3]-pos[1]) ; Convert to graphic coordinates
        
        ;; Initialize the text object
        textObject = obj_new('IGL_Text',xtitle, $
                             X=xloc, $
                             Y=yloc, $
                             ALIGNMENT=1, $
                             CHARSIZE=charsize*xcharsize, $
                             CLIP=0, $
                             COLOR=axiscolor, $
                             FONT=(font+1), $
                             THICKNESS=charthick, $
                             ORIENTATION=0.0 $
                            )
        
        ;; Get the graphic's coordinate system to assign it to the text object
        graphic -> GetProperty, Coord_Object=graphicCoords
        
        ;; Set object properties
        textObject -> SetProperty, Name='Axis Title', Coord_Object=graphicCoords
        
        ;; Add the title to the axis
        axis -> Add, textObject

     ENDIF

     ;; Add the axis to either the graphic or the owner of the
     ;; current coordinate system
     IF coords EQ currentCoords THEN parent -> Add, axis ELSE graphic -> Add, axis     

  ENDIF ELSE BEGIN 

     IF N_Elements(Yaxis) NE 0 THEN BEGIN ; Here we create a Y axis

        ;; Do we need a new coordinate system ?
        IF (ARRAY_EQUAL(yrange,yr) EQ 0) OR (Keyword_Set(ylog) NE Keyword_Set(yl)) OR (Keyword_Set(static) NE IsStatic) THEN BEGIN
           
           coords = obj_new('IGL_Coord', $
                            POSITION=pos, $
                            XRANGE=xr, $
                            YRANGE=yrange, $
                            XLOG=xl, $
                            YLOG=Keyword_Set(ylog), $
                            STATIC=Keyword_Set(static) $
                            )
           
        ENDIF ELSE coords = currentCoords
        
        ;; Calculate the position of the axis in the graphic
        IF N_Elements(xloc) NE 0 THEN BEGIN
           
           ;; First convert to normalized coordinates        
           IF Keyword_Set(Device) THEN xpos = xloc/float(Xsize)
           
           IF Keyword_Set(Normal) THEN xpos = xloc
           
           IF Keyword_Set(Data) OR (N_Elements(xpos) EQ 0) THEN xpos = (xr[1]-xr[0])/(pos[2]-pos[0])*xloc
           
           ;; Now convert to graphic coordinates
           xpos = (xpos-pos[0])/(pos[2]-pos[0])
           
        ENDIF ELSE BEGIN
           
           IF yaxis EQ 0 THEN xpos = 0.0
           IF yaxis EQ 1 THEN xpos = 1.0
           
        ENDELSE
        
        ;; Create the axis object
        axis = Obj_New('IGL_Axis', $
                       COORD_OBJECT=coords, $
                       XLOC=xpos, $
                       YLOC=ypos, $
                       DIRECTION=1, $
                       TICKDIR=Keyword_Set(yaxis), $
                       COLOR=color, $
                       CHARSIZE=(ycharsize*charsize), $
                       CHARTHICK=charthick, $
                       FONT=(font+1), $
                       GRIDSTYLE=ygridstyle, $
                       THICK=ythick, $
                       MINOR=yminor, $
                       TICKFORMAT=ytickformat, $
                       TICKNAME=ytickname, $
                       TICKINTERVAL=ytickinterval, $
                       TICKLAYOUT=yticklayout, $
                       TICKLEN=yticklen, $
                       TICKS=yticks, $
                       TICKUNITS=ytickunits, $
                       TICKV=ytickv $
                      )

        ;; Create the axis title if needed
        IF (ytitle NE '') THEN BEGIN

           ;; Calculate the title position
           yloc = 0.5
           middle = 0.5*(yrange[1]+yrange[0])
           IF middle NE 0 THEN ndigits = Floor(alog10(Abs(middle))+1) ELSE ndigits = 1
           xloc = pos[0]-(ndigits+2.5)*charsize*ycharsize*!D.X_CH_SIZE/float(Xsize)
           xloc = (xloc-pos[0])/(pos[2]-pos[0]) ; Convert to graphic coordinates
           
           ;; Initialize the text object
           textObject = obj_new('IGL_Text',ytitle, $
                                X=xloc, $
                                Y=yloc, $
                                ALIGNMENT=1, $
                                CHARSIZE=charsize*ycharsize, $
                                CLIP=0, $
                                COLOR=axiscolor, $
                                FONT=(font+1), $
                                THICKNESS=charthick, $
                                ORIENTATION=90.0 $
                               )

           ;; Get the graphic's coordinate system to assign it to the text object
           graphic -> GetProperty, Coord_Object=graphicCoords

           ;; Set object properties
           textObject -> SetProperty, Name='Axis Title', Coord_Object=graphicCoords

           ;; Add the title to the axis
           axis -> Add, textObject
     
        ENDIF               
        
        ;; Add the axis to either the graphic or the owner of the
        ;; current coordinate system
        IF coords EQ currentCoords THEN parent -> Add, axis ELSE graphic -> Add, axis

     ENDIF

  ENDELSE

  ;; Update the coordinate system
  IF Keyword_Set(save) THEN IGL_SetSysVars, 'currentCoord', coords  

  ;; Refresh the property editor
  currentWindow -> GetProperty, PropertyPanel=PropertyPanel
  IF OBJ_VALID(PropertyPanel) THEN PropertyPanel -> UpdateTreeBrowser
  
  ;; Draw window's content
  IF Keyword_set(nodraw) EQ 0 THEN currentWindow -> DrawWindow, /Buffered_Output

  ;; Restore device
  Set_Plot, entryDevice
  

END
